#pragma once

#include <algorithm/analytical_model/algorithm/cuda/math/vector/Common.cuh>
#include <algorithm/shared/utils/GenericVectorMath.h>
#include <utils/config.h>

inline HOT_FUNCTION __host__ __device__ auto make_float4(const float& s) -> float4
{
    return ::make_float4(s, s, s, s);
}

inline HOT_FUNCTION __host__ __device__ auto make_float4(const float2& a) -> float4
{
    return ::make_float4(a.x, a.y, 0.0F, 0.0F);
}

inline HOT_FUNCTION __host__ __device__ auto make_float4(const float2& a, const float& s) -> float4
{
    return ::make_float4(a.x, a.y, s, s);
}

inline HOT_FUNCTION __host__ __device__ auto make_float4(const float3& a) -> float4
{
    return ::make_float4(a.x, a.y, a.z, 0.0F); // discards w
}

inline HOT_FUNCTION __host__ __device__ auto make_float4(const float3& a, const float& w) -> float4
{
    return ::make_float4(a.x, a.y, a.z, w);
}

inline HOT_FUNCTION __host__ __device__ auto make_float4(const int3& a) -> float4
{
    return ::make_float4(float(a.x), float(a.y), float(a.z), 0.0F);
}

inline HOT_FUNCTION __host__ __device__ auto make_float4(const int4& a) -> float4
{
    return ::make_float4(float(a.x), float(a.y), float(a.z), float(a.w));
}

inline HOT_FUNCTION __host__ __device__ auto operator-(const float4& a) -> float4
{
    return solar::internal::vector::negate(a);
}

inline HOT_FUNCTION __host__ __device__ auto operator+(const float4& a, const float4& b) -> float4
{
    return solar::internal::vector::plus(a, b);
}

inline HOT_FUNCTION __host__ __device__ auto operator+(const float4& a, const float& b) -> float4
{
    return solar::internal::vector::plus(a, b);
}

inline HOT_FUNCTION __host__ __device__ auto operator+(const float& b, const float4& a) -> float4
{
    return solar::internal::vector::plus(a, b);
}

inline HOT_FUNCTION __host__ __device__ void operator+=(float4& a, const float4& b)
{
    solar::internal::vector::plusSelf(a, b);
}

inline HOT_FUNCTION __host__ __device__ auto operator-(const float4& a, const float4& b) -> float4
{
    return solar::internal::vector::minus(a, b);
}

inline HOT_FUNCTION __host__ __device__ auto operator-(const float4& a, const float& b) -> float4
{
    return solar::internal::vector::minus(a, b);
}

inline HOT_FUNCTION __host__ __device__ auto operator-(const float& b, const float4& a) -> float4
{
    return solar::internal::vector::minus(a, b);
}

inline HOT_FUNCTION __host__ __device__ void operator-=(float4& a, const float4& b)
{
    solar::internal::vector::minusSelf(a, b);
}

inline HOT_FUNCTION __host__ __device__ auto operator*(const float4& a, const float4& b) -> float4
{
    return solar::internal::vector::multiply(a, b);
}

inline HOT_FUNCTION __host__ __device__ auto operator*(const float4& a, const float& s) -> float4
{
    return solar::internal::vector::multiply(a, s);
}

inline HOT_FUNCTION __host__ __device__ auto operator*(const float& s, const float4& a) -> float4
{
    return solar::internal::vector::multiply(a, s);
}

inline HOT_FUNCTION __host__ __device__ void operator*=(float4& a, const float& s)
{
    solar::internal::vector::multiplySelf(a, s);
}

inline HOT_FUNCTION __host__ __device__ auto operator/(const float4& a, const float4& b) -> float4
{
#ifdef __CUDA_ARCH__
    return solar::internal::vector::cuda::divide(a, b);
#else
    return solar::internal::vector::divide(a, b);
#endif
}

inline HOT_FUNCTION __host__ __device__ auto operator/(const float4& a, const float& s) -> float4
{
#ifdef __CUDA_ARCH__
    return solar::internal::vector::cuda::divide(a, s);
#else
    return solar::internal::vector::divide(a, s);
#endif
}

inline HOT_FUNCTION __host__ __device__ auto operator/(const float& s, const float4& a) -> float4
{
#ifdef __CUDA_ARCH__
    return solar::internal::vector::cuda::divide(a, s);
#else
    return solar::internal::vector::divide(a, s);
#endif
}

inline HOT_FUNCTION __host__ __device__ void operator/=(float4& a, const float& s)
{
#ifdef __CUDA_ARCH__
    solar::internal::vector::cuda::divideSelf(a, s);
#else
    solar::internal::vector::divideSelf(a, s);
#endif
}

inline HOT_FUNCTION __device__ __host__ auto lerp(const float4& a, const float4& b,
                                                  const float& t) -> float4
{
    return solar::internal::vector::lerp(a, b, t);
}

inline HOT_FUNCTION __device__ __host__ auto clamp(const float4& v, const float& a,
                                                   const float& b) -> float4
{
    return solar::internal::vector::clamp(v, a, b);
}

inline HOT_FUNCTION __device__ __host__ auto clamp(const float4& v, const float4& a,
                                                   const float4& b) -> float4
{
    return solar::internal::vector::clamp(v, a, b);
}

inline HOT_FUNCTION __host__ __device__ auto dot(const float4& a, const float4& b) -> float
{
    return solar::internal::vector::dot(a, b);
}

inline HOT_FUNCTION __host__ __device__ auto length(const float4& v) -> float
{
    return solar::internal::vector::length(v);
}

inline HOT_FUNCTION __host__ __device__ auto rlength(const float4& v) -> float
{
    return solar::internal::vector::rlength(v);
}

inline HOT_FUNCTION __host__ __device__ auto normalize(const float4& v) -> float4
{
    return solar::internal::vector::normalize(v);
}

inline HOT_FUNCTION __host__ __device__ auto floor(const float4& v) -> float4
{
    return solar::internal::vector::floor(v);
}

inline HOT_FUNCTION __host__ __device__ auto reflect(const float4& i, const float4& n) -> float4
{
    return solar::internal::vector::reflect(i, n);
}

inline HOT_FUNCTION __host__ __device__ auto fabs(const float4& v) -> float4
{
    return solar::internal::vector::abs(v);
}

inline HOT_FUNCTION __host__ __device__ auto fmaxf(const float4& a, const float4& b) -> float4
{
    return solar::internal::vector::max(a, b);
}

inline HOT_FUNCTION __host__ __device__ auto fminf(const float4& a, const float4& b) -> float4
{
    return solar::internal::vector::min(a, b);
}
